#include "Rp2Instance.h"
#include "Rp2WglApplication.h"

#include "Rp23DSLoader.h"
#include "Rp2ObjLoader.h"
#include "Rp2R1Loader.h"

using namespace Rp2;

RP2_WINDOW_APPLICATION(Instance);


unsigned int gauiBufferObject[4];

//---------------------------------------------------------------------------------------------------
Instance::Instance()
	:
	WindowApplication(TC("Model"),100,50,640,480)
{
	m_bVsynOn = true;
	m_bDebugOn = false;

	// Initialize data 
	System::InsertDirectory("../Data/Model");

	// Animation
	m_dAnimTime = 0.0;
	m_dAnimTimeDelta = 0.01;

	// Water effect
    // Arbitrary constants to make particular looking waves.
    m_afWaveSpeed[0] = 0.05f;
    m_afWaveSpeed[1] = 0.01f;
    m_afWaveSpeed[2] = 0.02f;
    m_afWaveSpeed[3] = 0.08f;

    m_afWaveHeight[0] = -8.0f;
    m_afWaveHeight[1] = 10.0f;
    m_afWaveHeight[2] = 5.8f;
    m_afWaveHeight[3] = 8.5f;

    m_afBumpSpeed[0] = 0.031f;
    m_afBumpSpeed[1] = 0.04f;
    m_afBumpSpeed[2] = -0.03f;
    m_afBumpSpeed[3] = 0.02f;
}
//---------------------------------------------------------------------------------------------------
Instance::~Instance()
{
}
//---------------------------------------------------------------------------------------------------
void Instance::InitRenderStates()
{
	// Get OpenGL version
	const char *szVersionString = (const char *)glGetString(GL_VERSION);
	LOG("Your OpenGL Version: %s", szVersionString);

	// Vertical synchronization 
	wglSwapIntervalEXT(m_bVsynOn);
	glClearColor (0.0f, 0.5f, 0.75f, 1.0f);
/*	glClearColor (0.0f, 0.0f, 0.0f, 1.0f);*/
}
//---------------------------------------------------------------------------------------------------
bool Instance::OnInitialize()
{	
	if (!WindowApplication::OnInitialize())
	{
		return false;
	}

	// Set up scene
	CreateScene();

	// Initialize camera
    m_spkScene->UpdateGS();
	m_spkCamera->SetFrustum(60.0f, m_iWidth/(float)m_iHeight, 0.01f, 10000.0f);
	Vector3f kCDir;
    //Vector3f kCLoc(0.0f, 400.0f, -800.0f);
	Vector3f kCLoc(0.0f, 20.0f, -20.0f);
	//kCDir = -kCLoc;
 	kCDir = -kCLoc;
 	kCDir.Normalize();
	Vector3f kCRight(1.0f, 0.0f, 0.0f);
	kCRight.Normalize();
	Vector3f kCUp = kCDir.Cross(kCRight);
	kCUp.Normalize();
	m_spkCamera->SetFrame(kCLoc, kCRight, kCUp, kCDir);

	// initial update of objects
	m_spkScene->UpdateGS();
	m_spkScene->UpdateRS();

	// initialize culler state
	m_kCuller.SetCamera(m_spkCamera);
	m_kCuller.ComputeVisibleSet(m_spkScene);

	// performance measurement
	ResetTime();
	InitializeCameraMotion(1.0f, 0.02f);
	InitializeObjectMotion(m_spkScene);
	
	// init avi recorder
	m_bWritingAVI = false;
	m_kAVIGenerator.SetRate(30);

	// opengl framebuffer object
	

	return true;
}
//---------------------------------------------------------------------------------------------------
void Instance::OnTerminate()
{
    m_spkScene = 0;
    m_spkWireframe = 0;
    m_spkHeightField = 0;
    m_spkHeight = 0;
	m_spkTerrainEffect = 0;
	m_spkWaterEffect = 0;
	m_LightCam = 0;

	delete[] pkBoards;
	pkBoards = 0;
	WindowApplication::OnTerminate();
}
//---------------------------------------------------------------------------------------------------
void Instance::OnIdle()
{
	MeasureTime();	

	if (MoveCamera())
	{
		m_kCuller.ComputeVisibleSet(m_spkScene);
	}

	if (MoveObject())
	{
		m_spkScene->UpdateGS();
		m_kCuller.ComputeVisibleSet(m_spkScene);
	}
	
	//set time for Water Effect
//      float fTime = (m_bStopped ? m_fStopTime : (float)System::GetTime());
//      m_spkWaterEffect->SetTime(fTime);
	
	m_pkWglRenderer->ClearBuffers();
	
    // Draw the scene to the back buffer/
    if (m_pkWglRenderer->BeginScene())
    {
        // Draw the scene to a color buffer.
        m_pkPBuffer->Enable();
        //m_pkWglRenderer->SetClearColor(ColorRGBA::WHITE);
        glClearColor (0.0f, 0.0f, 0.0f, 1.0f);
        m_pkWglRenderer->ClearBuffers();
        m_pkWglRenderer->DrawScene(m_kCuller.GetVisibleSet());
        m_pkPBuffer->Disable();
// 		m_pkPBuffer->CopyToTexture(true);
// 		Image* pkImage = m_pkPBuffer->GetTarget()->GetImage();
// 		pkImage->Save("scene.wmif");

        // Draw the scene to the main window and also to a regular screen
        // polygon, placed in the lower-left corner of the main window.
        //m_pkWglRenderer->SetClearColor(ColorRGBA(0.5f,0.5f,0.5f,1.0f));
        glClearColor (0.0f, 0.5f, 0.75f, 1.0f);
		m_pkWglRenderer->ClearBuffers();
        m_pkWglRenderer->DrawScene(m_kCuller.GetVisibleSet());
        m_pkWglRenderer->SetCamera(m_spkScreenCamera);
		m_pkWglRenderer->Draw(m_spkScenePolygon);
        m_pkWglRenderer->SetCamera(m_spkCamera);
        DrawFrameRate(8,GetHeight() - 8,ColorRGBA::BLACK);
       // m_pkWglRenderer->EndScene();
	}

// 	if (m_pkWglRenderer->BeginScene())
// 	{
// 		m_pkWglRenderer->ClearBuffers();
// 		m_pkWglRenderer->SetCamera(m_spkCamera);	
// 		m_pkWglRenderer->DrawScene(m_kCuller.GetVisibleSet());
// 		DrawFrameRate(8, GetHeight() - 8, ColorRGBA::WHITE);
// 	}

	// avi recorder
	if (m_bWritingAVI)
	{	
		LPBITMAPINFOHEADER lpbih;
		lpbih = m_kAVIGenerator.GetBitmapHeader();
		glReadBuffer(GL_BACK);
		glReadPixels(0,0,lpbih->biWidth,lpbih->biHeight,GL_BGR_EXT,GL_UNSIGNED_BYTE,m_pBits); 
		m_kAVIGenerator.AddFrame(m_pBits);
	}

	m_pkWglRenderer->DisplayBackBuffer();
	
	UpdateFrameCount();
}
//---------------------------------------------------------------------------------------------------
void Instance::CreateScene()
{
	m_spkScene = new Node;
	m_spkWireframe = new WireframeState;
	m_spkScene->AttachGlobalState(m_spkWireframe);

	// FBO
	//RenderToTexture();

	// Material test
/*	CreateMaterialObj();*/
	
	// Create and attach lights
	AttachLights();

	// Load .R1 scene
/*	LoadR1(std::string("a2.R1"));*/
/*	LoadR1(std::string("skybox.R1"));*/

	// Create height field---------------------------------------------------------
/*    CreateHeightField();*/

	// Create flat floor
	CreateFloor();

	// Create water----------------------------------------------------------------
/*	CreateWaterEffect();*/
	
	// Add .3DS model --------------------------------------------------------------
/*	Create3DS(std::string("spaceship.3ds"));*/

	// Add .obj model --------------------------------------------------------------
	//LoadObj(std::string("arthaslichking_[19]_Stand_3276.obj"));


	// single texture
/*	CreateGeometry();*/
}
//---------------------------------------------------------------------------------------------------
void Instance::AttachLights()
{
	m_aspkLight[0] = new Light(Light::LT_DIRECTIONAL);
    float fValue = 0.75f;
	m_aspkLight[0]->Ambient = ColorRGB(fValue,fValue,fValue);
    fValue = -Mathf::Sqrt(1.0f/3.0f);
    m_aspkLight[0]->DVector = Vector3f(-1.0f, -1.0f, 1.0f);
	m_aspkLight[0]->DVector.Normalize();
    m_aspkLight[0]->Diffuse = ColorRGB(1.0f, 1.0f, 1.0f);
    m_aspkLight[0]->Specular = ColorRGB::WHITE;	
	m_aspkLight[0]->Exponent = 1.0f;

	m_spkScene->AttachLight(m_aspkLight[0]);
	m_spkScene->UpdateRS();  
}
//---------------------------------------------------------------------------------------------------
void Instance::RenderToTexture()
{

}
//---------------------------------------------------------------------------------------------------
void Instance::InitSM()
{
	m_LightCam = new Camera;
	m_LightCam->SetFrustum(60.0f, 1.0f, 1.0f, 100.0f);
	Vector3f vPos(50.0f, 50.0f, -50.0f);
	Vector3f kD(-1.0f, -1.0f, 1.0f);
	Vector3f kU, kR;
	Vector3f::GenerateOrthonormalBasis(kR, kU, kD);
	m_spkCamera->SetFrame(vPos, kR, kU, kD);
	
}
//---------------------------------------------------------------------------------------------------
void Instance::CreateMaterialObj()
{
    Node* pkTrnNode = new Node;
    m_spkScene->AttachChild(pkTrnNode);	

    Attributes kAttr;
    kAttr.SetPositionChannels(3);
    kAttr.SetTCoordChannels(0,2);
    VertexBuffer* pkVBuffer = new VertexBuffer(kAttr,4);
    pkVBuffer->Position3(0) = Vector3f(-1.0f,-1.0f,0.0f);
    pkVBuffer->Position3(1) = Vector3f(-1.0f, 1.0f,0.0f);
    pkVBuffer->Position3(2) = Vector3f( 1.0f, 1.0f,0.0f);
    pkVBuffer->Position3(3) = Vector3f( 1.0f,-1.0f,0.0f);
    pkVBuffer->TCoord2(0,0) = Vector2f(0.0f,0.0f);
    pkVBuffer->TCoord2(0,1) = Vector2f(1.0f,0.0f);
    pkVBuffer->TCoord2(0,2) = Vector2f(1.0f,1.0f);
    pkVBuffer->TCoord2(0,3) = Vector2f(0.0f,1.0f);

    IndexBuffer* pkIBuffer = new IndexBuffer(6);
    int* aiIndex = pkIBuffer->GetData();
    aiIndex[0] = 0;  aiIndex[1] = 3; aiIndex[2] = 1;
    aiIndex[3] = 3;  aiIndex[4] = 2; aiIndex[5] = 1;	

    // square with door texture
    TriMesh* pkDoor = new TriMesh(pkVBuffer,pkIBuffer);
    pkTrnNode->AttachChild(pkDoor);
    pkDoor->AttachEffect(new TextureEffect("Door.wmif"));	

    // Create a square with
    //   1. material that has diffuse alpha less than one
    //   2. alpha state to enable blending
    //   3. texture
    // The texture is combined with the material to produce a semitransparent
    // effect on the Sand.  You should be able to see Door through it.
    TriMesh* pkSand = new TriMesh(pkVBuffer,pkIBuffer);
    pkTrnNode->AttachChild(pkSand);
    pkSand->Local.SetTranslate(Vector3f(0.25f,0.25f,-0.25f));
    pkSand->AttachEffect(new MaterialTextureEffect("Sand.wmif"));
    m_spkMaterial = new MaterialState;
    m_spkMaterial->Diffuse = ColorRGB(1.0f,0.0f,0.0f);
    m_spkMaterial->Alpha = 0.5f;
    pkSand->AttachGlobalState(m_spkMaterial);
	
    // Create another square with material alpha less than one and with
    // a texture.
    TriMesh* pkWater = new TriMesh(pkVBuffer,pkIBuffer);
    pkTrnNode->AttachChild(pkWater);
    pkWater->Local.SetTranslate(Vector3f(0.5f,0.5f,-0.5f));
    pkWater->AttachEffect(new MaterialTextureEffect("Water.wmif"));
    MaterialState* pkWMaterial = new MaterialState;
    pkWMaterial->Diffuse = ColorRGB(0.0f,0.0f,1.0f);
    pkWMaterial->Alpha = 0.5f;
    pkWater->AttachGlobalState(pkWMaterial);

    // Blending needs to be enabled for material alpha.  It is possible to
    // allow the MaterialTextureEffect object to enable blending when it
    // detects that the material alpha value is smaller than one, but then
    // you are forced into blending.  You might prefer to have a situation
    // where certain events must occur in order to trigger the transparency,
    // in which case you would toggle the BlendEnabled flag yourself.
    AlphaState* pkAS = new AlphaState;
    pkAS->BlendEnabled = true;
    pkSand->AttachGlobalState(pkAS);
	pkWater->AttachGlobalState(pkAS);
}
//---------------------------------------------------------------------------------------------------
void Instance::LoadR1(std::string& rkFilename)
{
	Node* pkNode = new Node;
	R1Loader::Load(rkFilename, *pkNode);

	// get effect
	m_spkSkinEffect1 = (SkinningEffect*)pkNode->GetChild(0)->GetEffect(0);

	// get controller
	m_spkSKCtrl = (SKController*)m_spkSkinEffect1->GetController(0);

	m_spkScene->AttachChild(pkNode);

}
//---------------------------------------------------------------------------------------------------
void Instance::LoadObj(std::string& rkFilename)
{
	ObjLoader::LoadObj(rkFilename, m_akMesh);
	CullState* pkCull = new CullState;
	pkCull->Enabled = false;
	for (int i = 0; i < (int)m_akMesh.size(); i++)
	{
		m_akMesh[i]->AttachGlobalState(pkCull);
		m_spkScene->AttachChild(m_akMesh[i]);
	}
}
//---------------------------------------------------------------------------------------------------
void Instance::CreateFloor()
{
	Attributes kAttr;
	kAttr.SetPositionChannels(3);
	kAttr.SetNormalChannels(3);
	kAttr.SetTCoordChannels(0, 2);
	Transformation kTXFm;
	kTXFm.SetRotate(Matrix3f(Vector3f::UNIT_X, Mathf::PI*0.5f));
	kTXFm.SetTranslate(Vector3f(0.0f, 0, 0.0f));

	StandardMesh kSM = StandardMesh(kAttr, &kTXFm);

	m_spkFloor = kSM.Rectangle(20, 20, 10.0f, 10.0f);
	m_spkFloor->AttachEffect(new LightingMaterialEffect("Wall1.wmif"));
	m_spkScene->AttachChild(m_spkFloor);
//
    MaterialState* pkWMaterial = new MaterialState;
	pkWMaterial->Ambient = ColorRGB(0.0f, 0.0f, 0.0f);
    pkWMaterial->Diffuse = ColorRGB(1.0f,1.0f,1.0f);
	pkWMaterial->Specular = ColorRGB(1.0f, 1.0f, 1.0f);
	pkWMaterial->Shininess = 110.0f;
    pkWMaterial->Alpha = 1.0f;
    m_spkFloor->AttachGlobalState(pkWMaterial);

    // Blending needs to be enabled for material alpha.  It is possible to
    // allow the MaterialTextureEffect object to enable blending when it
    // detects that the material alpha value is smaller than one, but then
    // you are forced into blending.  You might prefer to have a situation
    // where certain events must occur in order to trigger the transparency,
    // in which case you would toggle the BlendEnabled flag yourself.
    AlphaState* pkAS = new AlphaState;
    pkAS->BlendEnabled = true;
    m_spkFloor->AttachGlobalState(pkAS);

    m_spkScreenCamera = new Camera;
    m_spkScreenCamera->Perspective = false;
	//m_spkScreenCamera->SetFrustum(-1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f);
	m_spkScreenCamera->SetFrustum(0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f);
    m_spkScreenCamera->SetFrame(Vector3f::ZERO,Vector3f::UNIT_X,
		Vector3f::UNIT_Y,Vector3f::UNIT_Z);	

    // Create a screen polygon to use as the RGBA render target.
    Attributes kAttr1;
    kAttr1.SetPositionChannels(3);
    kAttr1.SetTCoordChannels(0,2);
    VertexBuffer* pkVBuffer = new VertexBuffer(kAttr1,4);
    pkVBuffer->Position3(0) = Vector3f(0.0f,0.0f,0.0f);
    pkVBuffer->Position3(1) = Vector3f(0.2f,0.0f,0.0f);
    pkVBuffer->Position3(2) = Vector3f(0.2f,0.2f,0.0f);
    pkVBuffer->Position3(3) = Vector3f(0.0f,0.2f,0.0f);
    pkVBuffer->TCoord2(0,0) = Vector2f(0.0f,0.0f);
    pkVBuffer->TCoord2(0,1) = Vector2f(1.0f,0.0f);
    pkVBuffer->TCoord2(0,2) = Vector2f(1.0f,1.0f);
    pkVBuffer->TCoord2(0,3) = Vector2f(0.0f,1.0f);
    IndexBuffer* pkIBuffer = new IndexBuffer(6);
    int* aiIndex = pkIBuffer->GetData();
    aiIndex[0] = 0;  aiIndex[1] = 1;  aiIndex[2] = 2;
    aiIndex[3] = 0;  aiIndex[4] = 2;  aiIndex[5] = 3;
    m_spkScenePolygon = new TriMesh(pkVBuffer,pkIBuffer);

    // Create a red image for the purposes of debugging.  The image itself
    // should not show up because the frame-buffer object writes to the
    // texture memory.  But if this fails, the red image should appear.
    int iWidth = 256, iHeight = 256;

    Image::FormatMode eFormat = Image::IT_RGBA8888;

    m_spkSceneImage = new Image(eFormat,iWidth,iHeight,0,"SceneImage");
    // Create the texture effect for the scene polygon.  The resources are
    // loaded so that the scene target texture has everything needed for
    // FrameBuffer::Create to succeed.
    TextureEffect* pkEffect = new TextureEffect("SceneImage");
    m_pkSceneTarget = pkEffect->GetPTexture(0,0);
    m_pkSceneTarget->SetOffscreenTexture(true);
    m_spkScenePolygon->AttachEffect(pkEffect);
    m_spkScenePolygon->UpdateGS();
    m_spkScenePolygon->UpdateRS();
    m_pkWglRenderer->LoadResources(m_spkScenePolygon);

    // Create the RGBA frame-buffer object to be bound to the scene polygon.
    m_pkPBuffer = FrameBuffer::Create(m_eFormat,m_eDepth,m_eStencil,
        m_eBuffering,m_eMultisampling,m_pkWglRenderer,m_pkSceneTarget);
    assert(m_pkPBuffer);
	m_spkScenePolygon->VBuffer->PrintToFile("vB.txt");
	
    m_spkScene->UpdateGS();
}
//---------------------------------------------------------------------------------------------------
void Instance::Create3DS(std::string& rkFilename)
{
	TriMesh* pk3dsMesh =  TDSLoader::Load3DS(rkFilename);
	pk3dsMesh->AttachEffect(new TextureEffect("spaceship.tga"));
	// transform model
	Transformation kTXFm;
	kTXFm.SetUniformScale(3.0f);
// 	kTXFm.SetRotate(Matrix3f(Vector3f::UNIT_X, -Mathf::PI*0.5f));
// 	kTXFm.SetTranslate(Vector3f(0.0f, 50.0f, 20.0f));

	VertexBuffer* pkVB = pk3dsMesh->VBuffer;
	Attributes kAttr = pkVB->GetAttributes();
    int iVQuantity = pkVB->GetVQuantity();
    int i;
    for (i = 0; i < iVQuantity; i++)
    {
        pkVB->Position3(i) = kTXFm.ApplyForward(pkVB->Position3(i));
    }

    if (kAttr.HasNormal())
    {
        Vector3f kSave = kTXFm.GetTranslate();
        kTXFm.SetTranslate(Vector3f::ZERO);
        for (i = 0; i < iVQuantity; i++)
        {
            pkVB->Normal3(i) = kTXFm.ApplyForward(pkVB->Normal3(i));
            pkVB->Normal3(i).Normalize();
        }
        kTXFm.SetTranslate(kSave);
    }	
	
	CullState* pkCull = new CullState;
	pkCull->Enabled = false;
	pk3dsMesh->AttachGlobalState(pkCull);

	m_spkScene->AttachChild(pk3dsMesh);
}
//---------------------------------------------------------------------------------------------------
void Instance::CreateHeightField()
{
    Attributes kAttr;
    kAttr.SetPositionChannels(3);
    kAttr.SetTCoordChannels(0,2);
    kAttr.SetTCoordChannels(1,2);

    const int iXSamples = 128, iZSamples = 128;
    const float fXExtent = 1280.0f, fZExtent = 1280.0f;
    int iVQuantity = iXSamples*iZSamples;
    int iTQuantity = 2*(iXSamples-1)*(iZSamples-1);
    VertexBuffer* pkVB = new VertexBuffer(kAttr,iVQuantity);
    IndexBuffer* pkIB = new IndexBuffer(3*iTQuantity);

    // Generate the geometry for a flat height field.
    float fInv0 = 1.0f/(iXSamples - 1.0f);
    float fInv1 = 1.0f/(iZSamples - 1.0f);
    float fU, fV;
    int i, i0, i1;
    for (i1 = 0, i = 0; i1 < iZSamples; i1++)
    {
        fV = i1*fInv1;
        Vector3f kZTmp = ((2.0f*fV-1.0f)*fZExtent)*Vector3f::UNIT_Z;
        for (i0 = 0; i0 < iXSamples; i0++)
        {
            fU = i0*fInv0;
            Vector3f kXTmp = ((2.0f*fU-1.0f)*fXExtent)*Vector3f::UNIT_X;
            pkVB->Position3(i) = kXTmp + kZTmp + Vector3f(0.0f, -10.0f, 0.0f);
            Vector2f kTCoord(fU,fV);
            pkVB->TCoord2(0,i) = kTCoord;
            pkVB->TCoord2(1,i) = kTCoord;
            i++;
        }
    }

    // Generate the index array for a regular grid of squares, each square a
    // pair of triangles.
    int* aiIndex = pkIB->GetData();
    for (i1 = 0, i = 0; i1 < iZSamples - 1; i1++)
    {
        for (i0 = 0; i0 < iXSamples - 1; i0++)
        {
            int iV0 = i0 + iXSamples * i1;
            int iV1 = iV0 + 1;
            int iV2 = iV1 + iXSamples;
            int iV3 = iV0 + iXSamples;
            aiIndex[i++] = iV0;
            aiIndex[i++] = iV1;
            aiIndex[i++] = iV2;
            aiIndex[i++] = iV0;
            aiIndex[i++] = iV2;
            aiIndex[i++] = iV3;
        }
    }

    // Set the heights based on a precomputed height field.
    //m_spkHeight = Image::Load("BTHeightField.wmif");
	m_spkHeight = Image::Load("HeightMap.tga");	
	unsigned char* pucData = m_spkHeight->GetData();
	for (i = 0; i < iVQuantity; i++, pucData++)
    {
        unsigned char ucValue = *pucData;
        float fHeight = ((float)ucValue)/255.0f;
		//float fPerturb = 0.30f*Mathf::SymmetricRandom();	
	    //pkVB->Position3(i).Y() = 300.0f*fHeight + fPerturb;	
		pkVB->Position3(i).Y() = 600.0f*fHeight - 200;
		pkVB->TCoord2(0,i) *= 4.0f;
		pkVB->TCoord2(1,i) *= 8.0f;	
	}

    m_spkHeightField = new TriMesh(pkVB,pkIB);

	m_spkTerrainEffect = new BlendedTerrainEffect("BTGrass.wmif","BTStone.wmif");
	m_spkHeightField->AttachEffect(m_spkTerrainEffect);
    m_spkScene->AttachChild(m_spkHeightField);
}
//---------------------------------------------------------------------------------------------------
void Instance::CreateWaterEffect()
{
	m_bStopped = false;
	m_fStopTime = (float)System::GetTime();

    // The rectangle has origin (0,0,0) and up-direction of (0,0,-1).
    Attributes kAttr;
    kAttr.SetPositionChannels(3);
    kAttr.SetNormalChannels(3);
    kAttr.SetColorChannels(0,3);
    kAttr.SetTCoordChannels(0,2);
    Transformation kXFrm;
    kXFrm.SetRotate(Matrix3f(Vector3f::UNIT_X, 0.5f*Mathf::PI));
	StandardMesh kSM(kAttr, &kXFrm);
	TriMesh* pkMesh = kSM.Rectangle(10,10,4000.0f,4000.0f);
	m_spkScene->AttachChild(pkMesh);

    MaterialState* pkWMaterial = new MaterialState;
    pkWMaterial->Diffuse = ColorRGB(1.0f,1.0f,1.0f);
    pkWMaterial->Alpha = 0.4f;
    pkMesh->AttachGlobalState(pkWMaterial);

	AlphaState* pkAS = new AlphaState;
	pkAS->BlendEnabled = true;
	pkMesh->AttachGlobalState(pkAS);

	CreateNormalMapFromHeightImage("marble.tga");

	m_spkWaterEffect = new WaterEffect("NormalMap","WaterGradient.wmif",
        "RippleSky.wmif");
	pkMesh->AttachEffect(m_spkWaterEffect);

    // Fix the light direction for the entire simulation.
    Vector3f kLightDir(0.0f,-1.0f,-1.0f);
    kLightDir.Normalize();
    m_spkWaterEffect->SetLightDir(kLightDir);

    // Arbitrary constants to make particular looking waves.
    float afWaveDirX[4] = {0.25f,0.0f,-0.7f,-0.8f};
    float afWaveDirY[4] = {0.0f,0.15f,-0.7f,0.1f};
    float afWaveOffset[4] = {0.0f,0.2f,0.3f,-0.2f};
    m_spkWaterEffect->SetWaveDirX(afWaveDirX);
    m_spkWaterEffect->SetWaveDirY(afWaveDirY);
    m_spkWaterEffect->SetWaveOffset(afWaveOffset);

    m_spkWaterEffect->SetAverageDuDxDvDy(1.0f/24.0f);
    m_spkWaterEffect->SetAmbient(0.3f);
    m_spkWaterEffect->SetTextureRepeat(6.0f);

    m_fWaveSpeedAmplitude = 1.0f;
    m_fWaveHeightAmplitude = 1.0f;
    m_fBumpSpeedAmplitude = 1.0f;
    m_spkWaterEffect->SetWaveSpeed(m_afWaveSpeed);
    m_spkWaterEffect->SetWaveHeight(m_afWaveHeight);
    m_spkWaterEffect->SetBumpSpeed(m_afBumpSpeed);	

}
//---------------------------------------------------------------------------------------------------
void Instance::CreateNormalMapFromHeightImage(const char* acImageName)
{
    // Given a height field z = H(x,y), normal vectors to the graph of the
    // function are
    //   N(x,y) = (dH/dx,dH/dy,1)/sqrt(1+(dH/dx)^2+(dH/dy)^2)
    // The terms dH/dx and dH/dy are the partial derivatives of H with respect
    // to x and y.  These are approximated by centered differences
    //   dH/dx = (H(x+dx,y) - H(x-dx,y))/(2*dx)
    //   dH/dy = (H(x,y+dy) - H(x,y-dy))/(2*dy)
    // When the height field is stored in an image I(x,y), where 0 <= x < xmax
    // and 0 <= y < ymax (x and y are integers), choose dx = 1 and dy = 1.
    // The image is gray scale, so the red, green, and blue channels all have
    // the same value.  It is sufficient to use the red channel, R(x,y).
    // The approximations are
    //   dH/dx = (R(x+1,y) - R(x-1,y))/2
    //   dH/dy = (R(x,y+1) - R(x,y-1))/2
    // Special handling is required at the image boundaries x = 0, x = xmax-1,
    // y = 0, and y = ymax-1.  Wrap-around is used here, so define values
    //   R(-1,y) = R(xmax-1,y)
    //   R(xmax,y) = R(0,y)
    //   R(x,-1) = R(x,ymax-1)
    //   R(x,ymax) = R(x,0)
    //
    // Since the red channel has values in [0,255], the approximations for
    // dH/dx and dH/dy are in [-255/2,255/2].  In this particular application,
    // these are scaled to [-100,100] (WHY?).

	Image* pkPlasma = Image::Load(acImageName);
    assert(pkPlasma);

    int iXMax = pkPlasma->GetBound(0);
    int iYMax = pkPlasma->GetBound(1);
    int iXMaxM1 = iXMax -1, iYMaxM1 = iYMax - 1;
    int iBPP = pkPlasma->GetBytesPerPixel();
    unsigned char* aucHeight = pkPlasma->GetData();
    const float fScale = 100.0f/255.0f;
    unsigned char* aucNormal = new unsigned char[4*iXMax*iYMax];

    for (int iY = 0; iY < iYMax; iY++)
    {
        int iYm1 = (iY > 0 ? iY-1 : iYMaxM1);
        int iYp1 = (iY < iYMaxM1 ? iY+1 : 0);

        for (int iX = 0; iX < iXMax; iX++)
        {
            int iXm1 = (iX > 0 ? iX-1 : iXMaxM1);
            int iXp1 = (iX < iXMaxM1 ? iX+1 : 0);

            float fRXm1Y = (float)aucHeight[iBPP*(iXm1 + iY*iXMax)];
            float fRXp1Y = (float)aucHeight[iBPP*(iXp1 + iY*iXMax)];
            float fRXYm1 = (float)aucHeight[iBPP*(iX + iYm1*iXMax)];
            float fRXYp1 = (float)aucHeight[iBPP*(iX + iYp1*iXMax)];
            float fDHDX = 0.5f*(fRXp1Y - fRXm1Y);
            float fDHDY = 0.5f*(fRXYp1 - fRXYm1);

            Vector3f kNormal(fScale*fDHDX,fScale*fDHDY,1.0f);
            kNormal.Normalize();

            // Transform the normal vector from [-1,1]^3 to [0,255]^3 so it
            // can be stored as a color value.
            unsigned char* aucColor = &aucNormal[4*(iX+iY*iXMax)];
            aucColor[0] = (unsigned char)(127.5f*(kNormal[0]+1.0f));
            aucColor[1] = (unsigned char)(127.5f*(kNormal[1]+1.0f));
            aucColor[2] = (unsigned char)(127.5f*(kNormal[2]+1.0f));
            aucColor[3] = 0;
        }
    }

    delete pkPlasma;
    m_spkNormalMap = new Image(Image::IT_RGBA8888,iXMax,iYMax,aucNormal,
        "NormalMap");
}
//---------------------------------------------------------------------------------------------------
void Instance::CreateGeometry()
{
	Attributes kAttr;
	kAttr.SetPositionChannels(3);
	kAttr.SetNormalChannels(3);
	kAttr.SetTCoordChannels(0, 2);
	StandardMesh kSM = StandardMesh(kAttr);

	pkBoard0 = kSM.Rectangle(2, 2, 1.0f, 1.0f);
	pkBoard0->Local.SetTranslate(Vector3f(-2.2f, 2.0f, 0.0f));

	pkBoard1 = kSM.Rectangle(2, 2, 1.0f, 1.0f);
	pkBoard1->Local.SetTranslate(Vector3f(2.2f, 2.0f, 0.0f));


	pkBoard0->AttachEffect(new TextureEffect("sand.wmif"));
	pkBoard1->AttachEffect(new TextureEffect("Water.wmif"));

 	m_spkScene->AttachChild(pkBoard0);
 	m_spkScene->AttachChild(pkBoard1);
	// attach particles
	//m_pkParticles = new Particles;
	//m_pkParticles = CreateParticles();
	//m_spkScene->AttachChild(m_pkParticles);
}
//---------------------------------------------------------------------------------------------------
bool Instance::OnKeyDown(unsigned char ucKey, int iXPos, int iYPos)
{
	if (WindowApplication::OnKeyDown(ucKey, iXPos, iYPos))
	{
		return true;
	}

	switch(ucKey)
	{
	case 'V':
	case 'v':
		m_bVsynOn = !m_bVsynOn;
		wglSwapIntervalEXT(m_bVsynOn);
		return true;
	case 'R':
	case 'r':
		Tga::ScreenToTGA("ScreenShot.tga", m_pkWglRenderer);
		return true;
	case 't':
	{
		if (!m_bWritingAVI)
		{
			m_bWritingAVI = true;
			bool bResult;
			LPBITMAPINFOHEADER lpbih;		
			WindowApplication* pkTheApp = (WindowApplication*)TheApplication;
			// give info about bitmap
			lpbih = new BITMAPINFOHEADER;
			lpbih->biSize = sizeof(BITMAPINFOHEADER);
			lpbih->biWidth = pkTheApp->GetWidth();
			lpbih->biHeight = pkTheApp->GetHeight();
			lpbih->biPlanes = 1;
			lpbih->biBitCount = 24;
			lpbih->biSizeImage = ((lpbih->biWidth*lpbih->biBitCount+31)/32 * 4)*lpbih->biHeight; 
			lpbih->biCompression = BI_RGB;
			lpbih->biXPelsPerMeter = 0;
			lpbih->biYPelsPerMeter = 0;
			lpbih->biClrUsed = 0;
			lpbih->biClrImportant = 0;
			m_kAVIGenerator.SetBitmapHeader(lpbih);
			
			// allocating memory
			lpbih = m_kAVIGenerator.GetBitmapHeader();
			m_pBits = new BYTE[lpbih->biSizeImage];
			bResult = m_kAVIGenerator.InitAVIEngine();
			if (!bResult)
			{
				m_bWritingAVI = false;
				m_kAVIGenerator.ReleaseAVIEngine();
				delete[] m_pBits;
				assert(false);
			}

			return true;
		}

	}

	case 'T':
		if (m_bWritingAVI)
		{
			m_bWritingAVI = false;
			m_kAVIGenerator.ReleaseAVIEngine();
			delete[] m_pBits;
		}

		return true;

	case 'g':
		m_dAnimTime += m_dAnimTimeDelta;
		//std::cout << "m_dAnimTime :" << m_dAnimTime << std::endl;
		m_spkSKCtrl->Update(m_dAnimTime);
		m_spkScene->UpdateGS(m_dAnimTime);
		m_kCuller.ComputeVisibleSet(m_spkScene);
		return true;
	case 'G':
		m_dAnimTime = 0.0;
		m_spkScene->UpdateGS(m_dAnimTime);
		m_kCuller.ComputeVisibleSet(m_spkScene);
		std::cout << "Animation Reset" << std::endl;
		return true;
	case 'm':
	case 'M':
		m_spkWireframe->Enabled = !m_spkWireframe->Enabled;
	case 'o':
		m_bDebugOn = !m_bDebugOn;
		return true;
	}

	return false;
}
//---------------------------------------------------------------------------------------------------
void Instance::Terminate()
{
	delete TheApplication;
	TheApplication = 0;
}
//---------------------------------------------------------------------------------------------------
Particles* Instance::CreateParticles()
{
	const int iVQuantity = 32;
	Vector3f* akVertex = new Vector3f[iVQuantity];
	float* afSize = new float[iVQuantity];
	for (int i = 0; i < iVQuantity; i++)
	{
		akVertex[i].X() = Mathf::SymmetricRandom();
		akVertex[i].Y() = Mathf::SymmetricRandom();
		akVertex[i].Z() = Mathf::SymmetricRandom();
		afSize[i] = 0.25f * Mathf::UnitRandom();
	}
	Vector3fArray* pkVertices = new Vector3fArray(iVQuantity, akVertex);
	FloatArray* pkSizes = new FloatArray(iVQuantity, afSize);

	Attributes kAttr;
	kAttr.SetPositionChannels(3);
	kAttr.SetTCoordChannels(0, 2);
	Particles* pkParticles = new Particles(kAttr, pkVertices, pkSizes);

	Effect* pkEffect = new TextureEffect("Star.TGA");
	pkParticles->AttachEffect(pkEffect);

	AlphaState* pkAS = new AlphaState;
	pkAS->BlendEnabled = true;
	pkParticles->AttachGlobalState(pkAS);

	ZBufferState* pkZS = new ZBufferState;
	pkZS->Enabled = true;
	pkParticles->AttachGlobalState(pkZS);

	return pkParticles;
}
//---------------------------------------------------------------------------------------------------
